import * as THREE from "three";
import ThreeBSP from "../libs/ThreeBSP";
import { buildCube } from "../utils";

/**
 * 楼层
 */
export default class Storey {
  constructor() {
    // 创建一个空容器用于放置楼层中的元素
    this.mesh = new THREE.Object3D();
    // 创建地板
    this.buildFloor();
    // 创建墙
    this.buildWalls();
    // 创建门
    // this.buildDoors();
    // 创建窗户
    this.buildWindows();
  }

  /**
   * 创建地板
   */
  buildFloor() {
    // 创建地板并添加到楼层组中（如果在loader加载完成后才创建地板会导致事件无法绑定）
    const floorGeometry = new THREE.BoxGeometry(2600, 1, 1400);
    const floor = new THREE.Mesh(floorGeometry);
    floor.name = "地面";
    this.mesh.add(floor);
    // 加载地板材质
    const loader = new THREE.TextureLoader();
    loader.load("./texture/floor.jpg", texture => {
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      texture.repeat.set(10, 10);
      floor.material = new THREE.MeshLambertMaterial({
        map: texture,
        side: THREE.DoubleSide
      });
    });
  }

  /**
   * 创建墙
   */
  buildWalls() {
    const wallMaterial = new THREE.MeshPhongMaterial({ color: 0xafc0ca });
    this.mesh.add(
      buildCube(10, 240, 1400, 0, wallMaterial, -1295, 120, 0, "左墙面")
    );
    this.mesh.add(
      buildCube(10, 240, 1400, 0, wallMaterial, 1295, 120, 0, "右墙面")
    );
    this.mesh.add(
      buildCube(10, 240, 2600, 0.5, wallMaterial, 0, 120, -700, "后墙面")
    );
    // 创建前方墙面
    const frontWall = buildCube(
      10,
      240,
      2600,
      0.5,
      wallMaterial,
      0,
      120,
      700,
      "前墙面"
    );
    // 墙面镂空处配置（用于安装门窗）
    const hollows = [
      // [200, 180, 10, 0, wallMaterial, -600, 90, 700, "前门1"],
      // [200, 180, 10, 0, wallMaterial, 600, 90, 700, "前门2"],
      [100, 100, 10, 0, wallMaterial, -900, 90, 700, "窗户1"],
      [100, 100, 10, 0, wallMaterial, 900, 90, 700, "窗户2"],
      [100, 100, 10, 0, wallMaterial, -200, 90, 700, "窗户3"],
      [100, 100, 10, 0, wallMaterial, 200, 90, 700, "窗户4"]
    ];
    // 生成镂空处的立方体
    const hollowCubes = hollows.map(hollow => buildCube.apply(null, hollow));
    // 创建前墙面镂空后的立方体并添加到楼层整体结构中
    this.mesh.add(this.createResultBSP(frontWall, hollowCubes));
  }

  /**
   * 墙上挖门窗，通过两个几何体生成BSP对象
   * @param {object} srcMesh 原网格对象
   * @param {object} destMeshs 目标网格对象
   */
  createResultBSP(srcMesh, destMeshs) {
    destMeshs = Array.isArray(destMeshs) ? destMeshs : [destMeshs];
    var BSP = new ThreeBSP(srcMesh);
    destMeshs.forEach(destMesh => {
      const destBSP = new ThreeBSP(destMesh);
      // 进行差集计算
      BSP = BSP.subtract(destBSP);
    });
    // 从BSP对象内获取到处理完后的mesh模型数据
    var result = BSP.toMesh(srcMesh.material);
    // 更新模型的面和顶点的数据以及模型名称
    result.geometry.computeFaceNormals();
    result.geometry.computeVertexNormals();
    result.name = srcMesh.name;
    return result;
  }

  /**
   * 创建门
   */
  buildDoors() {
    const loader = new THREE.TextureLoader();
    const leftDoorTexture = loader.load("./texture/door_left.png");
    const rightDoorTexture = loader.load("./texture/door_right.png");
    const leftDoorMaterial = new THREE.MeshBasicMaterial({
      map: leftDoorTexture,
      opacity: 1.0,
      transparent: true
    });
    const rightDoorMaterial = new THREE.MeshBasicMaterial({
      map: rightDoorTexture,
      opacity: 1.0,
      transparent: true
    });
    this.mesh.add(
      buildCube(100, 180, 2, 0, leftDoorMaterial, -700, 90, 700, "左门1", 50)
    );
    this.mesh.add(
      buildCube(100, 180, 2, 0, rightDoorMaterial, -500, 90, 700, "右门1", -50)
    );
    this.mesh.add(
      buildCube(100, 180, 2, 0, leftDoorMaterial, 500, 90, 700, "左门2", 50)
    );
    this.mesh.add(
      buildCube(100, 180, 2, 0, rightDoorMaterial, 700, 90, 700, "右门2", -50)
    );
  }

  /**
   * 创建窗户
   */
  buildWindows() {
    const loader = new THREE.TextureLoader();
    const windowTexture = loader.load("./texture/window.png");
    const windowMaterial = new THREE.MeshBasicMaterial({
      map: windowTexture,
      opacity: 1.0,
      transparent: true
    });
    this.mesh.add(
      buildCube(100, 100, 2, 0, windowMaterial, -900, 90, 700, "窗户")
    );
    this.mesh.add(
      buildCube(100, 100, 2, 0, windowMaterial, 900, 90, 700, "窗户")
    );
    this.mesh.add(
      buildCube(100, 100, 2, 0, windowMaterial, -200, 90, 700, "窗户")
    );
    this.mesh.add(
      buildCube(100, 100, 2, 0, windowMaterial, 200, 90, 700, "窗户")
    );
  }
}

window.Storey = Storey;
